package com.giggle.restclient.util;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author guozichen
 * @ClassName:
 * @Description: (rsa算法)
 * @date 2020/11/12 20:23
 */
public class RSAUtils {

    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";
    private static final int MAX_ENCRYPT_BLOCK = 117;
    private static final int MAX_DECRYPT_BLOCK = 128;

    public RSAUtils() {
    }

    public static Map<String, Key> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
        Map<String, Key> keyMap = new HashMap(2);
        keyMap.put("RSAPublicKey", publicKey);
        keyMap.put("RSAPrivateKey", privateKey);
        return keyMap;
    }

    public static String[] genKeyPairString() throws Exception {
        String[] keyPair = new String[2];
        Map<String, Key> keyMap = genKeyPair();
        keyPair[0] = getPrivateKey(keyMap);
        keyPair[1] = getPublicKey(keyMap);
        return keyPair;
    }

    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = base64Decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(privateK);
        signature.update(data);
        return base64Encode(signature.sign());
    }

    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        byte[] keyBytes = base64Decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(base64Decode(sign));
    }

    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
        byte[] keyBytes = base64Decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(2, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Throwable throwable = null;

        try {
            int offSet = 0;

            byte[] cache;
            for(int i = 0; inputLen - offSet > 0; offSet = i * 128) {
                if (inputLen - offSet > 128) {
                    cache = cipher.doFinal(encryptedData, offSet, 128);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }

                out.write(cache, 0, cache.length);
                ++i;
            }

            byte[] decryptedData = out.toByteArray();
            cache = decryptedData;
            return cache;
        } catch (Throwable var21) {
            throwable = var21;
            throw var21;
        } finally {
            if (out != null) {
                if (throwable != null) {
                    try {
                        out.close();
                    } catch (Throwable var20) {
                        throwable.addSuppressed(var20);
                    }
                } else {
                    out.close();
                }
            }

        }
    }

    public static byte[] decryptByPrivateKey(String base64EncryptedData, String privateKey) throws Exception {
        byte[] base64DecodeData = base64Decode(base64EncryptedData);
        return decryptByPrivateKey(base64DecodeData, privateKey);
    }

    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
        byte[] keyBytes = base64Decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(2, publicK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Throwable throwable = null;

        try {
            int offSet = 0;

            byte[] cache;
            for(int i = 0; inputLen - offSet > 0; offSet = i * 128) {
                if (inputLen - offSet > 128) {
                    cache = cipher.doFinal(encryptedData, offSet, 128);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }

                out.write(cache, 0, cache.length);
                ++i;
            }

            byte[] decryptedData = out.toByteArray();
            cache = decryptedData;
            return cache;
        } catch (Throwable var21) {
            throwable = var21;
            throw var21;
        } finally {
            if (out != null) {
                if (throwable != null) {
                    try {
                        out.close();
                    } catch (Throwable var20) {
                        throwable.addSuppressed(var20);
                    }
                } else {
                    out.close();
                }
            }

        }
    }

    public static byte[] decryptByPublicKey(String base64EncryptedData, String publicKey) throws Exception {
        byte[] decodeData = base64Decode(base64EncryptedData);
        return decryptByPublicKey(decodeData, publicKey);
    }

    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        byte[] keyBytes = base64Decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(1, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Throwable throwable = null;

        try {
            int offSet = 0;

            byte[] cache;
            for(int i = 0; inputLen - offSet > 0; offSet = i * 117) {
                if (inputLen - offSet > 117) {
                    cache = cipher.doFinal(data, offSet, 117);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }

                out.write(cache, 0, cache.length);
                ++i;
            }

            byte[] encryptedData = out.toByteArray();
            cache = encryptedData;
            return cache;
        } catch (Throwable var21) {
            throwable = var21;
            throw var21;
        } finally {
            if (out != null) {
                if (throwable != null) {
                    try {
                        out.close();
                    } catch (Throwable var20) {
                        throwable.addSuppressed(var20);
                    }
                } else {
                    out.close();
                }
            }

        }
    }

    public static String encryptByPublicKeyString(byte[] data, String publicKey) throws Exception {
        byte[] encryptData = encryptByPublicKey(data, publicKey);
        return base64Encode(encryptData);
    }

    public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = base64Decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(1, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Throwable throwable = null;

        try {
            int offSet = 0;

            byte[] cache;
            for(int i = 0; inputLen - offSet > 0; offSet = i * 117) {
                if (inputLen - offSet > 117) {
                    cache = cipher.doFinal(data, offSet, 117);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }

                out.write(cache, 0, cache.length);
                ++i;
            }

            byte[] encryptedData = out.toByteArray();
            cache = encryptedData;
            return cache;
        } catch (Throwable var21) {
            throwable = var21;
            throw var21;
        } finally {
            if (out != null) {
                if (throwable != null) {
                    try {
                        out.close();
                    } catch (Throwable var20) {
                        throwable.addSuppressed(var20);
                    }
                } else {
                    out.close();
                }
            }

        }
    }

    public static String encryptByPrivateKeyString(byte[] data, String privateKey) throws Exception {
        byte[] encryptData = encryptByPrivateKey(data, privateKey);
        return base64Encode(encryptData);
    }

    public static String getPrivateKey(Map<String, Key> keyMap) throws Exception {
        Key key = (Key)keyMap.get("RSAPrivateKey");
        return base64Encode(key.getEncoded());
    }

    public static String getPublicKey(Map<String, Key> keyMap) throws Exception {
        Key key = (Key)keyMap.get("RSAPublicKey");
        return base64Encode(key.getEncoded());
    }

    private static String base64Encode(byte[] bytes) {
        return Base64.getEncoder().encodeToString(bytes);
    }

    private static byte[] base64Decode(String base64Data) {
        return Base64.getDecoder().decode(base64Data);
    }
}
